﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Web.Services.Description;
using System.Net;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using System.IO;


namespace Assign6
{
    class WSDLReader
    {

        public ServiceDescription serviceDescription;
        public XmlSchemas externalSchemas;
        public List<WebMethodCollection> webMethodCollections;
        

        //debug
        public StringBuilder debug = new StringBuilder();


        public List<WebMethodCollection> MethodCollections
        {
            get { return webMethodCollections; }
        }

        private void initialCollection()
        {
            webMethodCollections = new List<WebMethodCollection>();
            externalSchemas = new XmlSchemas();
            debug.Append("DEBUG:");
        }
        public WSDLReader(String filename)
        {
            initialCollection();
            XmlTextReader wsdl = new XmlTextReader(filename);
            serviceDescription = ServiceDescription.Read(wsdl);
            parseWSDL();
        }
        public WSDLReader(XmlTextReader wsdl)
        {
            initialCollection();
            serviceDescription = ServiceDescription.Read(wsdl);
            parseWSDL();
        }
       
        public void parseWSDL()
        {
            if (serviceDescription != null)
            {
                foreach (Service service in serviceDescription.Services)
                {
                    foreach (Port port in service.Ports)
                    {
                        WebMethodCollection webMethodCollection = new WebMethodCollection();
                        webMethodCollection.setServiceName(service.Name);
                        StringBuilder endpoint = new StringBuilder();
                        foreach (var bind in port.Extensions)
                        {
                            if (bind is HttpAddressBinding)
                            {
                                endpoint.Append(((HttpAddressBinding)bind).Location);
                            }
                            else if (bind is Soap12AddressBinding)
                            {
                                endpoint.Append(((Soap12AddressBinding)bind).Location);
                            }
                            else if (bind is SoapAddressBinding)
                            {
                                endpoint.Append(((SoapAddressBinding)bind).Location);
                            }
                        }
                        webMethodCollection.setWebServiceEndpoint(endpoint.ToString());


                        string bindingName = port.Binding.Name;
                        webMethodCollection.setBindingName(bindingName);
                        foreach (Binding binding in serviceDescription.Bindings)
                        {
                            if (binding.Name.Equals(bindingName))
                            {
                                string bindingType = binding.Type.Name;
                                webMethodCollection.setPortTypeName(bindingType);
                                foreach (PortType portType in serviceDescription.PortTypes)
                                {
                                    if (portType.Name.Equals(bindingType))
                                    {
                                        foreach (Operation operation in portType.Operations)
                                        {
                                            string operationName = operation.Name;
                                            string inputMessageName = operation.Messages.Input.Message.Name;
                                            string outputMessageName = operation.Messages.Output.Message.Name;
                                            WebMethod.Parameter[] inputParameter = analyzeMessage(inputMessageName);
                                            WebMethod.Parameter[] outputParameter = analyzeMessage(outputMessageName);
                                            webMethodCollection.Add(new WebMethod(operationName, inputParameter, outputParameter,inputMessageName,outputMessageName));
                                        }
                                        if (webMethodCollection.Count >= 0)
                                        {
                                            webMethodCollections.Add(webMethodCollection);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }



        private List<WebMethod.Parameter> analyzeElement(string elementName)
        {
            List<WebMethod.Parameter> childElements = new List<WebMethod.Parameter>();
            foreach (XmlSchema externSchema in externalSchemas)
            {
                if (childElements.Count == 0)
                {
                    foreach (var item in externSchema.Items)
                    {
                        if (item is XmlSchemaSimpleType)
                        {
                            XmlSchemaSimpleType type = item as XmlSchemaSimpleType;
                            XmlSchemaSimpleTypeRestriction restriction = (XmlSchemaSimpleTypeRestriction)type.Content ;
                            if (type.Name.Equals(elementName))
                            {
                                WebMethod.Parameter parameter = new WebMethod.Parameter("base", restriction.BaseTypeName.Name);
                                childElements.Add(parameter);
                            }
                        }
                        else if (item is XmlSchemaComplexType)
                        {
                            XmlSchemaComplexType type = item as XmlSchemaComplexType;
                            if (type.Name.Equals(elementName))
                            {
                               
                                XmlSchemaSequence sequence = (XmlSchemaSequence)type.Particle;
                                if (sequence != null)
                                {
                                    foreach (var e in sequence.Items)
                                    {
                                        if (e is XmlSchemaElement)
                                        {

                                            WebMethod.Parameter childParameter;
                                            XmlSchemaElement childElement = e as XmlSchemaElement;

                                            childParameter = new WebMethod.Parameter(childElement.Name, childElement.SchemaTypeName.Name);
                                            childParameter.elements.AddRange(analyzeElement(childElement.SchemaTypeName.Name));
                                            childElements.Add(childParameter);
                                        }

                                    }
                                }
                                else
                                {
                                    XmlSchemaContentType content = type.ContentType;
                                    XmlSchemaContentModel model = type.ContentModel;
                                    if (model != null)
                                    {
                                        XmlSchemaComplexContent complex = model as XmlSchemaComplexContent;
                                        XmlSchemaComplexContentExtension extension = complex.Content as XmlSchemaComplexContentExtension;
                                        if (extension != null)
                                        {
                                            WebMethod.Parameter extensionParameter = new WebMethod.Parameter("base", extension.BaseTypeName.Name);
                                            extensionParameter.elements.AddRange(analyzeElement(extension.BaseTypeName.Name));
                                            childElements.Add(extensionParameter);
                                            XmlSchemaSequence extensionSequence = (XmlSchemaSequence)extension.Particle;
                                            if (extensionSequence != null)
                                            {
                                                foreach (var ee in extensionSequence.Items)
                                                {
                                                    if (ee is XmlSchemaElement)
                                                    {
                                                        WebMethod.Parameter childParameter;
                                                        XmlSchemaElement childElement = ee as XmlSchemaElement;

                                                        childParameter = new WebMethod.Parameter(childElement.Name, childElement.SchemaTypeName.Name);
                                                        childParameter.elements.AddRange(analyzeElement(childElement.SchemaTypeName.Name));
                                                        childElements.Add(childParameter);
                                                    }
                                                }
                                            }
                                        }
                                    } 
                                }
                            }
                        }else if (item is XmlSchemaElement)
                        {
                            XmlSchemaElement element = item as XmlSchemaElement;
                            if (element.Name.Equals(elementName))
                            {
                                WebMethod.Parameter parameter;
                                if (element.SchemaType is XmlSchemaSimpleType)
                                {
                                    parameter = new WebMethod.Parameter("base", element.SchemaTypeName.Name);
                                    childElements.Add(parameter);
                                }
                                else if (element.SchemaType is XmlSchemaComplexType)
                                {
                                    XmlSchemaSequence sequence = ((XmlSchemaSequence)((XmlSchemaComplexType)element.SchemaType).Particle);
                                    if (sequence != null)
                                    {

                                        foreach (var e in sequence.Items)
                                        {
                                            if (e is XmlSchemaElement)
                                            {
                                                WebMethod.Parameter childParameter;
                                                XmlSchemaElement childElement = e as XmlSchemaElement;
                                                childParameter = new WebMethod.Parameter(childElement.Name, childElement.SchemaTypeName.Name);
                                                childParameter.elements.AddRange(analyzeElement(childElement.SchemaTypeName.Name));
                                                childElements.Add(childParameter);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return childElements;
        }
        public WebMethod.Parameter[] analyzeMessage(string messageName)
        {
            List<WebMethod.Parameter> parameters = new List<WebMethod.Parameter>();
            if (serviceDescription != null)
            {
                getExternalSchemas();
                foreach (Message message in serviceDescription.Messages)
                {
                    if (message.Name.Equals(messageName))
                    {
                        foreach (MessagePart part in message.Parts)
                        {
                            WebMethod.Parameter parameter;
                            string parameterName = part.Name;
                            string parameterType;
                            if (!part.Type.IsEmpty)
                            {
                                parameterType = part.Type.Name;
                                parameter = new WebMethod.Parameter(parameterName, parameterType);
                                parameters.Add(parameter);
                            }
                            else if (!part.Element.IsEmpty)
                            {
                                parameterType = part.Element.Name;
                                parameter = new WebMethod.Parameter(parameterName, parameterType);
                                parameter.elements.AddRange(analyzeElement(parameterType));
                                parameters.Add(parameter);
                            }
                        }
                    }
                }

            }
            return parameters.ToArray();
        }

        public void getExternalSchemas()
        {
            if (serviceDescription != null)
            {
                Types types = serviceDescription.Types;
                foreach (XmlSchema schema in types.Schemas)
                {
                    foreach (XmlSchemaObject externalSchema in schema.Includes)
                    {
                        if (externalSchema is System.Xml.Schema.XmlSchemaInclude)
                        {
                            Uri baseUri = new Uri(schema.TargetNamespace);
                            Uri schemaUri = new Uri(baseUri, ((XmlSchemaInclude)externalSchema).SchemaLocation);
                            WebClient client = new WebClient();
                            Stream externalSchemaStream = client.OpenRead(schemaUri);
                            XmlSchema realExternalSchema = XmlSchema.Read(externalSchemaStream, null);
                            externalSchemas.Add(realExternalSchema);
                        }
                        else if (externalSchema is System.Xml.Schema.XmlSchemaImport)
                        {
                            Uri baseUri = new Uri(schema.TargetNamespace);
                            Uri schemaUri = new Uri(baseUri, ((XmlSchemaImport)externalSchema).SchemaLocation);
                            WebClient client = new WebClient();
                            Stream externalSchemaStream = client.OpenRead(schemaUri);
                            XmlSchema realExternalSchema = XmlSchema.Read(externalSchemaStream, null);
                            externalSchemas.Add(realExternalSchema);
                        }
                    }
                    externalSchemas.Add(schema);
                }
            }


        }
    }


}
